package com.yanfan.data.service.impl;

import cn.hutool.core.lang.Snowflake;
import cn.hutool.core.util.IdUtil;
import com.yanfan.common.utils.DateUtils;
import com.yanfan.common.utils.StringUtils;
import com.yanfan.data.domain.Snmp;
import com.yanfan.data.domain.SnmpOid;
import com.yanfan.data.domain.SnmpResponse;
import com.yanfan.data.mapper.SnmpMapper;
import com.yanfan.data.mapper.SnmpOidMapper;
import com.yanfan.data.service.ISnmpResponseService;
import com.yanfan.data.service.SnmpService;
import com.yanfan.data.tdengine.service.TDSnmpResponseService;
import org.snmp4j.CommunityTarget;
import org.snmp4j.PDU;
import org.snmp4j.TransportMapping;
import org.snmp4j.event.ResponseEvent;
import org.snmp4j.mp.SnmpConstants;
import org.snmp4j.smi.*;
import org.snmp4j.transport.DefaultUdpTransportMapping;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

@Service
public class SnmpServiceImpl implements SnmpService {
    @Autowired
    private SnmpMapper snmpMapper;

    @Autowired
    private ISnmpResponseService responseService;


    @Value("${spring.datasource.druid.tdengine-server.enabled}")
    private Boolean enabled;

    @Autowired
    private SnmpOidMapper snmpOidMapper;

    @Autowired
    private TDSnmpResponseService tdSnmpResponseService;


    /**
     * 查询snmp协议主表
     *
     * @param id snmp协议主表主键
     * @return snmp协议主表
     */
    @Override
    public Snmp selectSnmpById(Long id) {
        return snmpMapper.selectSnmpById(id);
    }

    /**
     * 查询snmp协议主表列表
     *
     * @param snmp snmp协议主表
     * @return snmp协议主表
     */
    @Override
    public List<Snmp> selectSnmpList(Snmp snmp) {
        List<Snmp> snmps = snmpMapper.selectSnmpList(snmp);//一条数据
        for (Snmp s : snmps) {
            Snmp snmp1 = snmpMapper.selectSnmpById(s.getId());//3条
            s.setSnmpOidList(snmp1.getSnmpOidList());
        }
        return snmps;
    }

    /**
     * 新增snmp协议主表
     *
     * @param snmp snmp协议主表
     * @return 结果
     */
    @Transactional
    @Override
    public int insertSnmp(Snmp snmp) {
        snmp.setCreateTime(DateUtils.getNowDate());
        int rows = snmpMapper.insertSnmp(snmp);
        insertSnmpOid(snmp);
        return rows;
    }

    /**
     * 修改snmp协议主表
     *
     * @param snmp snmp协议主表
     * @return 结果
     */
    @Transactional
    @Override
    public int updateSnmp(Snmp snmp) {
        snmp.setUpdateTime(DateUtils.getNowDate());
        snmpMapper.deleteSnmpOidBySnmpId(snmp.getId());
        insertSnmpOid(snmp);
        return snmpMapper.updateSnmp(snmp);
    }

    /**
     * 批量删除snmp协议主表
     *
     * @param ids 需要删除的snmp协议主表主键
     * @return 结果
     */
    @Transactional
    @Override
    public int deleteSnmpByIds(Long[] ids) {
        snmpMapper.deleteSnmpOidBySnmpIds(ids);
        return snmpMapper.deleteSnmpByIds(ids);
    }

    /**
     * 删除snmp协议主表信息
     *
     * @param id snmp协议主表主键
     * @return 结果
     */
    @Transactional
    @Override
    public int deleteSnmpById(Long id) {
        snmpMapper.deleteSnmpOidBySnmpId(id);
        return snmpMapper.deleteSnmpById(id);
    }

    /**
     * 新增snmp协议OID子表信息
     *
     * @param snmp snmp协议主表对象
     */
    public void insertSnmpOid(Snmp snmp) {
        List<SnmpOid> snmpOidList = snmp.getSnmpOidList();
        Long id = snmp.getId();
        if (StringUtils.isNotNull(snmpOidList)) {
            List<SnmpOid> list = new ArrayList<SnmpOid>();
            for (SnmpOid snmpOid : snmpOidList) {
                snmpOid.setSnmpId(id);
                snmpOid.setIpAddress(snmp.getIpAddress());
                snmpOid.setCommunity(snmp.getCommunity());
                snmpOid.setCreateTime(DateUtils.getNowDate());
                list.add(snmpOid);
            }
            if (!list.isEmpty()) {
                snmpMapper.batchSnmpOid(list);
            }
        }
    }

    /**
     * @param ipAddress
     * @return
     */
    @Override
    public Snmp selectSnmpBySnmpIpAddress(String ipAddress) {

        return snmpMapper.selectSnmpBySnmpIpAddress(ipAddress);
    }

    /**
     * Snmp定时任务
     */
    @Override
    @Transactional
    public void fetchsnmpListGet() {

        // 获取所有的设备和OID
        List<SnmpOid> snmpOids = snmpOidMapper.selectSnmpOidList(new SnmpOid());
        if (null != snmpOids && !snmpOids.isEmpty()) {
            for (SnmpOid snmpOid : snmpOids) {
                // 获取应答PDU
                ResponseEvent res = this.getMessageByIpAndOid(snmpOid.getIpAddress(), snmpOid.getOid(), snmpOid.getCommunity());
                if (null != res && null != res.getResponse()) {
                    PDU response = res.getResponse();
                    Address peerAddress = res.getPeerAddress();

                    SnmpResponse snmpResponse = new SnmpResponse();
                    snmpResponse.setOidName(snmpOid.getOidName());
                    snmpResponse.setPeerAddress(peerAddress.toString());

                    for (int i = 0; i < response.size(); i++) {
                        // 获取响应结果
                        VariableBinding vb = response.get(i);
                        System.out.println("vb: " + vb);

                        snmpResponse.setOid(vb.getOid().toString());
                        snmpResponse.setVariable(vb.getVariable().toString());
                        if (enabled) {
                            // 生成19位的唯一id
                            Snowflake snowflake = IdUtil.getSnowflake(6, 2);
                            Long id = snowflake.nextId();
                            snmpResponse.setId(id.toString());

                            // 使用时序数据库保存数据
                            tdSnmpResponseService.insertSnmpResponse(snmpResponse);

                        } else {
                            responseService.insertSnmpResponse(snmpResponse);
                        }


                        System.out.println(vb.getOid() + " = " + vb.getVariable());
                    }
                }

            }
        }
    }

    // 使用SNMP协议获取设备信息
    public ResponseEvent getMessageByIpAndOid(String ip, String oid, String community) {
        String variableString = "";

        ResponseEvent responseEvent = null;
        try {
            //设定CommunityTarget
            CommunityTarget myTarget = new CommunityTarget();
            //机器的地址
            Address address = GenericAddress.parse("udp:" + ip + "/161");
            //设定地址
            myTarget.setAddress(address);
            //设置snmp共同体
            myTarget.setCommunity(new OctetString(community));
            //设置超时重试次数
            myTarget.setRetries(2);
            //设置超时的时间
            myTarget.setTimeout(5 * 60);
            //设置使用的snmp版本
            myTarget.setVersion(SnmpConstants.version2c);
            //设定采取的协议
            TransportMapping<UdpAddress> transport = new DefaultUdpTransportMapping();
            //调用TransportMapping中的listen()方法，启动监听进程，接收消息，由于该监听进程是守护进程，最后应调用close()方法来释放该进程
            transport.listen();
            //创建SNMP对象，用于发送请求PDU
            org.snmp4j.Snmp protocol = new org.snmp4j.Snmp(transport);
            //创建请求pdu,获取mib
            PDU request = new PDU();
            //调用的add方法绑定要查询的OID
            request.add(new VariableBinding(new OID(oid)));
            //调用setType()方法来确定该pdu的类型
            request.setType(PDU.GETNEXT);
            //调用 send(PDU pdu,Target target)发送pdu，返回一个ResponseEvent对象
            responseEvent = protocol.send(request, myTarget);

            //通过ResponseEvent对象来获得SNMP请求的应答pdu，方法：public PDU getResponse()
            PDU response = responseEvent.getResponse();

            //输出
            if (response != null) {
                System.out.println("---------------------");
                //通过应答pdu获得mib信息（之前绑定的OID的值），方法：VaribleBinding get(int index)
                VariableBinding vb = response.get(0);
                System.out.println(vb);
                variableString = String.valueOf(vb.getVariable());

            } else {
                System.out.println("Timeout: No response from agent.");
                System.out.println("Error: " + responseEvent.getError());
            }
            //调用close()方法释放该进程
            transport.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return responseEvent;
    }
}
